package com.lion.seaman.orm.jpa;

import org.springframework.data.jpa.domain.Specification;

import javax.persistence.criteria.*;
import javax.persistence.criteria.CriteriaBuilder.In;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

@SuppressWarnings({"rawtypes", "unchecked"})
public class LionSpecification<T> implements Specification<T> {
    private Collection<SearchFilter> filters;

    public LionSpecification(Collection<SearchFilter> filters) {
        this.filters = filters;
    }

    @Override
    public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query,
                                 CriteriaBuilder builder) {
        if (filters != null && !filters.isEmpty()) {
            List<Predicate> predicates = new ArrayList<Predicate>();
            List<Predicate> orPredicates = new ArrayList<Predicate>();
            for (SearchFilter filter : filters) {
                // nested path translate, 如Task的名为"user.name"的filedName, 转换为Task.user.name属性
                String[] names = filter.fieldName.split("\\.");
                Path expression = root.get(names[0]);
                for (int i = 1; i < names.length; i++) {
                    expression = expression.get(names[i]);
                }

                // logic operator
                switch (filter.operator) {
                    case EQ:
                        predicates.add(builder.equal(expression, filter.value));
                        break;
                    case LIKE:
                        predicates.add(builder.like(expression, "%" + filter.value + "%"));
                        break;
                    case GT:
                        predicates.add(builder.greaterThan(expression, (Comparable) filter.value));
                        break;
                    case LT:
                        predicates.add(builder.lessThan(expression, (Comparable) filter.value));
                        break;
                    case GTE:
                        predicates.add(builder.greaterThanOrEqualTo(expression, (Comparable) filter.value));

                        break;
                    case LTE:
                        predicates.add(builder.lessThanOrEqualTo(expression, (Comparable) filter.value));
                        break;
                    case NE:
                        predicates.add(builder.notEqual(expression, (Comparable) filter.value));
                        break;
                    case ISNULL:
                        predicates.add(builder.isNull(expression));
                        break;
                    case IN:
                        Collection value = (Collection) filter.value;
                        Iterator iterator = value.iterator();
                        In in = builder.in(expression);
                        while (iterator.hasNext()) {
                            in.value(iterator.next());
                        }
                        predicates.add(in);
                        break;
                    default:
                        break;
                }
            }
            if (orPredicates.size() > 0) {
                if (orPredicates.size() == 1) {
                    return builder.or(orPredicates.get(0), builder.and(builder.and(predicates.toArray(new Predicate[predicates.size()]))));
                }
                Predicate or = builder.or(orPredicates.toArray(new Predicate[orPredicates.size()]));
                predicates.add(or);
            }
            return builder.and(predicates.toArray(new Predicate[predicates.size()]));
        }
        return builder.conjunction();
    }

}
